"""
-------------------------------------------------
  Author:hxy
  Date:2022-09-06 14:47
  Des:sub
-------------------------------------------------
"""
# 这段代码是一个Python脚本，主要用于订阅MQTT消息并处理接收到的数据
# set PYTHONPATH=D:\pycharm\pywork\django-vue-admin_project\iot_project\backend
import sys
print('sys.path', sys.path)
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
import django
django.setup()

import datetime
import threading
import paho.mqtt.client as mqtt
from threading import Thread
import logging
import pymssql
from iot.models import Gradation, Cabinet, Point, Unit
from common.tdengine import load_tdengine_pool

_logger = logging.getLogger(__file__)
# host = '192.168.0.244'
host = '39.98.122.153'
TEMP_TYPE = 'T'


class Sub(Thread):
    # 方法初始化MQTT客户端，并设置连接和消息处理回调。
    def __init__(self, topic, username, password, host, port, client_id):
        super().__init__()
        self.topic = topic
        self.client = mqtt.Client(client_id)
        self.client.username_pw_set(username, password)
        self.client.connect(host=host, port=port)
        self.client.on_connect = self.on_connect
        self.client.on_message = self.on_message
        self.state = True
        self.lt = None

    def on_connect(self, client, userdata, flags, rc):
        rc = str(rc)
        if rc == '0':
            self.client.subscribe(self.topic)
            print("emqx链接成功")
        else:
            _logger.info(f'连接失败: 状态码为{rc}')

    def on_message(self, client, userdata, msg):
        try:
            data_list = eval(msg.payload.decode('utf-8'))
            if data_list:
                # 从data_list中提取设备ID。
                equipment = data_list['Device_ID']
                #  从data_list中移除设备ID键值对，以便剩下的数据只包含传感器数据。
                print(equipment)
                data_list.pop('Device_ID')
                print(data_list)
                # 遍历data_list中的每个键（即每个传感器的标识）
                for key in data_list.keys():
                    # 将传感器标识（假设格式为"gradation_type_ABC"）分割成列表，其中包含层级、类型和ABC代码。
                    sensor = key.split('_')
                    # 从data_list中提取该传感器的值。
                    value = data_list[key]
                    #  将传感器值四舍五入到两位小数。
                    value = round(value, 2)
                    # 构建一个包含传感器数据的字典，其中包含层级、类型、设备ID、值、当前时间和点标识。
                    data = {
                        'ABC': sensor[2],
                        'gradation': sensor[0],
                        'type': sensor[1],
                        'equipment': equipment,
                        'value': value if value < 90 else 0,
                        # 'value': value,
                        'time': datetime.datetime.now(),
                        'point': key
                    }
                    print(data)
                    threading.Thread(target=Sub.write_to_td, args=(data,)).start()
        except Exception as e:
            _logger.error(f'e: {e}')

    @staticmethod
    # 使用@staticmethod装饰器定义一个静态方法write_to_td，这意味着这个方法不需要对类实例进行操作，它只处理传入的参数。
    def write_to_td(data_list):
        # 从传入的data_list字典中提取出point、equipment、gradation、time、value、type和ABC等字段。
        point = data_list['point']
        equipment = data_list['equipment']
        gradation = data_list['gradation']
        time = data_list['time']
        value = data_list['value']
        type = data_list['type']
        ABC = data_list['ABC']
        # 根据设备ID和点名称构造TDengine数据库中的表名。
        table = equipment + '_' + point
        # 写入PostgreSQL数据库
        Sub.write_to_pg(equipment, gradation, table, value, type, ABC, time)
        if type == TEMP_TYPE:
            # 条件写入SQL Server数据库
            Sub.write_to_server(table, value)
        # 写入TDengine数据库
        with load_tdengine_pool().get() as cursor:
            sql = f"INSERT INTO {table} USING iot TAGS ('{equipment}') VALUES ('{time}', {value});"
            cursor.execute(sql)

    @staticmethod
    def write_to_pg(equipment, location, table, value, type, ABC, time):
        # 查询数据库中是否有与 equipment 参数匹配的配电站记录。如果没有找到，记录一条信息日志并返回。
        cabinet = Cabinet.objects.filter(code=equipment)
        if not cabinet:
            _logger.info('请配置相关配电站')
            return
        #  查询数据库中是否有与 location 参数匹配的电压或温度层级记录。如果没有找到，记录一条信息日志并返回。
        gradation = Gradation.objects.filter(code=location)
        if not gradation:
            _logger.info('请配置相关电压、温度层级属性')
            return
        unit = Unit.objects.filter(type=type)
        if not unit:
            _logger.info('请配置相关单位类型')
            return
        points = Point.objects.filter(code=table)
        if not points:
            Point.objects.create(gradation=gradation.first(), unit=unit.first(),
                                 cabinet=cabinet.first(), **{'code': table, 'value': value,
                                                             'name': ABC + gradation.first().name,
                                                             'dept_belong_id': cabinet.first().dept_belong_id})
        else:
            Point.update(table, **{'value': value, 'update_datetime': time})

    @staticmethod
    def write_to_server(sensor, value):
        # 使用pymssql库连接到SQL Server数据库。这里需要提供数据库的主机名、用户名、密码和数据库名。
        conn = pymssql.connect(host, 'sa', '<Aa@23456>', 'Scada')
        # 创建一个游标对象，用于执行SQL语句。
        cursor = conn.cursor()
        # 执行一个SQL查询，查找RTable表中tagname字段等于传入的sensor参数的记录。fetchall()方法用于获取所有查询结果。
        cursor.execute(f"select * from RTable WHERE tagname='{sensor}';")
        rows = cursor.fetchall()
        if len(rows) == 0:
            # 如果没有找到记录，则执行插入操作，将新的tagname和tagvalue插入到RTable表中，并提交事务
            cursor.execute(f"insert into Rtable(tagname, tagvalue) values('{sensor}', {value});")
            conn.commit()
        else:
            # 如果找到了匹配的记录，则执行更新操作，将tagvalue更新为新的值，并提交事务。
            cursor.execute(f"UPDATE RTable set tagvalue={value} WHERE tagname='{sensor}';")
            conn.commit()
        cursor.close()
        conn.close()

    def run(self):
        self.client.loop_forever()


if __name__ == '__main__':
    Sub(topic='iot/temp1', username='emqx', password='public', host='39.98.122.153', port=1883, client_id='iot_sub').start()
